import sys
import shutil
import re
import util


# --- Utility functions ---

def ReadFile(filename):
    file = open(filename, "rt")
    try:
        lines = file.read()
    finally:
        file.close()
    return lines


def WriteFile(filename, lines):
    file = open(filename, "w")
    try:
        lines = file.write(lines)
    finally:
        file.close()
        

def InsertBefore(s_place, s_update, s_text):
    return s_text.replace(s_place, s_update + s_place)
    


def InsertAfter(s_place, s_update, s_text):
    return s_text.replace(s_place, s_place + s_update)


# --- Update files in /src dir ---

def update_json_delay_js():
    """Create nested JSON.AST object with functions """
    
    """N.B This function is not repeated.
    Have to manually copy over fresh json-delay from backup
    to retest"""

    # file to update
    s_filename = util.get_write_path("json-delay.js")
    #print(s_filename)
    
    # get the code to append to json-delay.js
    s_append = ReadFile("src/_json-delay.js")
    #print(s_append);
    
    # append the code - only if hasn't already been appended
    if not (ReadFile(s_filename).find("SetupAST") == -1):
        #print("json-delay.js already updated")
        return
        
    fileHandle = open(s_filename, 'a' )
    fileHandle.write("\n\n" + s_append)
    fileHandle.close()


def update_runtime_h():
    """Add runtime function declaration"""
    
    # get the code to add
    s_update = ReadFile("src/_runtime.h")
    
    # filename
    s_filename = "runtime.h"
    
    # Add the code before the Pseudo functions section
    s_replace_text = "  /* Pseudo functions"
    s_text = ReadFile(util.get_read_path(s_filename))
    s_text = s_text.replace(s_replace_text, s_update + s_replace_text)
    WriteFile(util.get_write_path(s_filename), s_text)


def update_runtime_cc():
    """Add runtime function body"""
    
    # 1) Add runtime function body
    
    # get the code to add
    s_update = ReadFile("src/_runtime.cc")
    
    # filename
    s_filename = "runtime.cc"
    
    # Add the code before the Pseudo functions section
    s_place = """// ----------------------------------------------------------------------------
// Implementation of Runtime"""
    s_text = ReadFile(util.get_read_path(s_filename))
    #s_text = s_text.replace(s_replace_text, s_update + s_replace_text)
    s_text = InsertBefore(s_place, s_update, s_text)

    # 2) Add the header include of the code the runtime function calls
    
    # TODO: update if replace prettyprinter.cc with json-ast-builder.h
    #include "json-ast.h"
    s_header_update = "\n#include \"prettyprinter.h\""
    s_header_place = """
#include "runtime.h"
"""
    s_text = InsertBefore(s_header_place, s_header_update, s_text)
    
    # 3) fix Runtime_CompileString to use is_json arg set to null
    # to set the enum value JSON_AST
    
    # Place and update
    s_place = """Compiler::ValidationState validate = (is_json->IsTrue())
    ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;"""
    s_update = "\n" + """  validate = (is_json->IsNull())
    ? Compiler::JSON_AST : validate;"""
    
    s_text = InsertAfter(s_place, s_update, s_text)
    
    # 4) Write the updates to runtime.cc
    
    WriteFile(util.get_write_path(s_filename), s_text)


def update_SConscript():
    """add json-ast-token.cc to SOURCES"""
    
    # file to update
    s_filename = "SConscript"
    
    # Place to and new
    s_place = "zone.cc\n"
    s_update = "    json-ast-token.cc\n"
    
    # Do the update
    s_text = ReadFile(util.get_read_path(s_filename))
    s_text = InsertAfter(s_place, s_update, s_text)
    WriteFile(util.get_write_path(s_filename), s_text)


def update_compiler_h():
    """add JSON_AST tag to enum ValidationState"""
    #enum ValidationState { VALIDATE_JSON, DONT_VALIDATE_JSON, JSON_AST };

    # file to update
    s_filename = "compiler.h"
    
    # Place and update
    s_place = "DONT_VALIDATE_JSON"
    s_update = ", JSON_AST"
    
    # Do the update
    s_text = ReadFile(util.get_read_path(s_filename))
    s_text = InsertAfter(s_place, s_update, s_text)
    WriteFile(util.get_write_path(s_filename), s_text)


def update_compiler_cc():
    """fix MakeFunction() to call MakeAST() or MakeASTJsonML() depending
       using the Validation enum and the JSON_AST tag"""

    # file to update
    s_filename = "compiler.cc"
    
    # Place and update
    s_place = """FunctionLiteral* lit = MakeAST(is_global, script, extension, pre_data);"""
    s_update = """FunctionLiteral* lit = (validate == Compiler::JSON_AST)
    ? MakeASTJsonML(is_global, script, extension, pre_data)
    : MakeAST(is_global, script, extension, pre_data);"""
    
    # Do the update - a complete replace this time
    s_text = ReadFile(util.get_read_path(s_filename))
    s_text = s_text.replace(s_place, s_update)
    WriteFile(util.get_write_path(s_filename), s_text)


def update_parser_h():
    """add #include to add MakeJsonML()"""

    # file to update
    s_filename = "parser.h"
    
    # Place and update
    s_place = "\n} }  // namespace v8::internal"
    s_update = """#include "json-ast-parser.h"\n\n"""
    
    # Do the update
    s_text = ReadFile(util.get_read_path(s_filename))
    s_text = InsertBefore(s_place, s_update, s_text)
    WriteFile(util.get_write_path(s_filename), s_text)


def update_parser_cc():
    """add #include "json-ast-token.h" 
       add #include "json-ast-parser.cc"
    """

    # file to update
    s_filename = "parser.cc"
    
    # Place and update
    s_place = """#include "string-stream.h"\n"""
    s_update = """#include "json-ast-token.h"\n"""
    s_place2 = "#undef NEW"
    s_update2 = """#include "json-ast-parser.cc"\n\n"""
    
    # Do the update
    s_text = ReadFile(util.get_read_path(s_filename))
    s_text = InsertAfter(s_place, s_update, s_text)
    s_text = InsertBefore(s_place2, s_update2, s_text)
    s_text = update_parser_cc_2_hack(s_text)
    WriteFile(util.get_write_path(s_filename), s_text)


def update_prettyprinter_h():
    """add #include "json-ast-token.h" 
       add #include "json-ast-builder.h" - to include the JsonAstBuilder class
    """

    # file to update
    s_filename = "prettyprinter.h"
    
    # Place and update
    s_place = """#include "ast.h"\n"""
    s_update = """#include "json-ast-token.h"\n"""
    s_place2 = "\n// The JSON AST builder"
    s_update2 = """#include "json-ast-builder.h"\n\n"""
    
    # Do the update
    s_text = ReadFile(util.get_read_path(s_filename))
    s_text = InsertAfter(s_place, s_update, s_text)
    s_text = InsertBefore(s_place2, s_update2, s_text)
    s_text = update_prettyprinter_h_2(s_text)
    WriteFile(util.get_write_path(s_filename), s_text)


def update_prettyprinter_cc():
    """add #include "json-ast-builder.cc" """

    # file to update
    s_filename = "prettyprinter.cc"
    
    # Place and update
    s_place = "#endif  // DEBUG"
    s_update = """#include "json-ast-builder.cc"\n\n\n"""
    
    # Do the update
    s_text = ReadFile(util.get_read_path(s_filename))
    s_text = InsertBefore(s_place, s_update, s_text)
    WriteFile(util.get_write_path(s_filename), s_text)


def update_prettyprinter_h_2(s_text):
    """ change 'private' to 'protected' in class JsonAstBuilder to allow subclass
        JsonAstBuilder to access members """

    p = re.compile(r"(class JsonAstBuilder.*?)(private)",re.M | re.S)
    (r,n) = p.subn(r"\1protected", s_text)
    #print r
    #print n
    if n == 0:
            print("Error - prettyprinter.h : private keyword not found")
    elif n > 1:
            print "Error - prettyprinter.h : %i private keywords updated - 1 expected" % n
    return r  


def update_parser_cc_2_hack(s_text):
    """update ParseVariableDeclarations() so that "var x" is added to the 
       variable init block as an "Id" (VariableProxy) node. Currently only
       "var x = 1" assignments are added.
    """
    s_place = """  } while (peek() == Token::COMMA);"""
    s_update = """
    // HACK - add an Id/VariableProxy to block to record a var without an
    // initializer in non-global mode. e.g var x;  // var a = 2, x;
    if (!is_pre_parsing_ && !top_scope_->is_global_scope() && value == NULL) {
      //if (block) block->AddStatement(NEW(ExpressionStatement(assignment)));
      if (block) block->AddStatement(NEW(ExpressionStatement(last_var)));
    }
    // END HACK""" + "\n"
    
    # Do the update
    s_text = InsertBefore(s_place, s_update, s_text)
    return s_text


# call the main function
if __name__ == "__main__":
    print("======== lib.py should not be called directly ==========")

